home *** CD-ROM | disk | FTP | other *** search
/ Best Tools for JAVA / Best Tools for JAVA.iso / JAVA_ALL / IDE / SUBARTIC / SUB_ARCT / LIB / MENUBAR.JAV < prev    next >
Encoding:
Text File  |  1996-10-04  |  16.3 KB  |  566 lines

  1. package sub_arctic.lib;
  2. import sub_arctic.output.style;
  3. import sub_arctic.output.style_manager;
  4. import sub_arctic.output.drawable;
  5. import sub_arctic.output.loaded_image;
  6. import sub_arctic.input.pressable;
  7. import sub_arctic.input.event;
  8.  
  9. import java.util.Vector;
  10. import java.util.Hashtable;
  11. import java.awt.Font;
  12. import java.awt.Rectangle;
  13. import java.awt.Point;
  14.  
  15. /**
  16.  * This class implements a style-neutral menu bar. It should be able to
  17.  * handle mac, windoze, and motif/unix style menu bars, but not NextStep
  18.  * style. 
  19.  * @author Ian Smith
  20.  */
  21. public class menubar 
  22. extends base_interactor implements pressable, menu_notifier {
  23.  
  24.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  25.   /**
  26.    * This is where we store the cached value of our width
  27.    */
  28.   protected int cached_width=-1;
  29.  
  30.   /**
  31.    * This is where we hold the hotspots for the menubar's left objects
  32.    */
  33.   protected Vector left_hotspots;
  34.  
  35.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  36.  
  37.   /**
  38.    * This is where we hold the hotspots for the menubar's right objects
  39.    */
  40.   protected Vector right_hotspots;
  41.  
  42.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  43.  
  44.   /**
  45.    * Our current menubar image.
  46.    */
  47.   protected loaded_image bar_image;
  48.  
  49.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  50.  
  51.   /**
  52.    * This hashtable keeps track of the mapping from the menu items
  53.    * to the buttons on the menu.
  54.    */
  55.   protected Hashtable button_to_menu;
  56.  
  57.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  58.  
  59.   /**
  60.    * Left hand set of items.
  61.    */
  62.   protected Vector left_items;
  63.  
  64.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  65.  
  66.   /**
  67.    * Right hand set of items.
  68.    */
  69.   protected Vector right_items;
  70.  
  71.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  72.  
  73.   /**
  74.    * Left hand set of images
  75.    */
  76.   protected Vector left_images;
  77.  
  78.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  79.  
  80.   /**
  81.    * Right hand set of images
  82.    */
  83.   protected Vector right_images;
  84.  
  85.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  86.  
  87.   /**
  88.    * This is the number of the currently depressed item. If no item
  89.    * is currently depressed, this value will be -1.
  90.    */
  91.   protected int _currently_depressed=-1;
  92.  
  93.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  94.  
  95.   /**
  96.    * Which item is currently depressed? This returns -1 if there
  97.    * is no currently depressed item.
  98.    *
  99.    * @return int the number of the currently depressed item.
  100.    */
  101.   public int currently_depressed() {return _currently_depressed;}
  102.  
  103.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  104.  
  105.   /**
  106.    * Set the currently depressed item. This may involve the popping
  107.    * up of menus.
  108.    * @param int     i   the item number you want depressed.
  109.    * @param boolean t   true if the item is in the left hand set of objects.
  110.    * @param event   evt the event which is causing this button to be depressed.
  111.    */
  112.   public void set_currently_depressed(int i, boolean t, event evt) {
  113.     _currently_depressed=i;
  114.     set_current_is_left(t);
  115.     Object tmp;
  116.     menu m;
  117.     Point p;
  118.  
  119.     /* look for item on the left side */
  120.     if (_current_is_left) {
  121.       /* find the item */
  122.       tmp=left_items.elementAt(_currently_depressed);
  123.     } else {
  124.       /* its on the right side */
  125.       tmp=right_items.elementAt(_currently_depressed);
  126.     }
  127.  
  128.     /* is there a menu for this object? */
  129.     if (!button_to_menu.containsKey(tmp)) {
  130.       System.err.println("No menu for that item!");
  131.       return;
  132.     }
  133.  
  134.     /* it is there, extract it */
  135.     m=(menu)button_to_menu.get(tmp);
  136.  
  137.     /* derive the coordinates of the new menu in global coords*/
  138.     if (_current_is_left) {
  139.       p=compute_menu_location(((Rectangle)left_hotspots.
  140.                    elementAt(_currently_depressed)),m);
  141.     } else {
  142.       p=compute_menu_location(((Rectangle)right_hotspots.
  143.                    elementAt(_currently_depressed)),m);
  144.     }
  145.  
  146.     /* set the coordinates to those returned*/
  147.     m.set_x(p.x);
  148.     m.set_y(p.y);
  149.     get_top_level().add_child(m);
  150.     
  151.     /* tell the menu agent to go */
  152.     menu.agent().set_focus_to(m,evt,null,this);
  153.     
  154.     /* we are now damaged */
  155.     damage_self();
  156.   }
  157.  
  158.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  159.  
  160.   /**
  161.    * This is a boolean which says if the selected item is in the right
  162.    * or the left hand set. This value should only be consulted when
  163.    * the value of _current is not -1.
  164.    */
  165.   protected boolean _current_is_left=true;
  166.  
  167.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  168.  
  169.   /**
  170.    * Retrieve a boolean indicating if the currently selected item
  171.    * is on the left.
  172.    *
  173.    * @return boolean returns true if the item is in the left hand set of 
  174.    *                 objects.
  175.    */
  176.   public boolean current_is_left() { return _current_is_left;}
  177.  
  178.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  179.  
  180.   /**
  181.    * Set the flag indicating if the currently selected item is on the left.
  182.    * @param boolean n the new value.
  183.    */
  184.   public void set_current_is_left(boolean n) { _current_is_left=n;}
  185.   
  186.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  187.  
  188.   /**
  189.    * Create a menubar from two Vectors of Strings. 
  190.    * @param Vector left the strings to start the menus with from the left.
  191.    * @param Vector right the strings to start the menus with from the right.
  192.    * @param int    w     width of the menu bar.
  193.    */
  194.   public menubar(Vector left, Vector right,int w) {
  195.     super(0,0,w,10);
  196.  
  197.     int i;
  198.     button_to_menu=new Hashtable();
  199.  
  200.     /* we are just going to assume we don't need a copy here */
  201.     /* we know that the style code will copy them for us */
  202.     left_items=left;
  203.     right_items=right;
  204.   }
  205.   
  206.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  207.  
  208.   /**
  209.    * This function is called to derive the font from the default
  210.    * font. This is here as a hook for easy overriding.
  211.    *
  212.    * @param Font f the default font.
  213.    * @return Font the font to use on the menubar.
  214.    */
  215.   public Font derive_font(Font f) {
  216.     return new Font(f.getName(), f.getStyle(), f.getSize()+2);
  217.   }
  218.   
  219.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  220.  
  221.   /**
  222.    * Make the menubar images, given a set of left and right objects.
  223.    * @param Vector left the array of strings or images for the left menus
  224.    * @param Vector right the array of strings or images for the right menus
  225.    * @param int w the width of the bar 
  226.    */
  227.   public void style_changed() {
  228.     loaded_image bar;
  229.     style cs=style_manager.current_style();
  230.     int w=w(),i;
  231.     Vector left_tmp, right_tmp;
  232.  
  233.     /* we have to make a copy of the items here because we are going to
  234.      * muck with these items in the first one in a minute*/
  235.     left_tmp=new Vector();
  236.     left_tmp.setSize(left_items.size());
  237.     for(i=0; i<left_items.size(); ++i) {
  238.       left_tmp.setElementAt(left_items.elementAt(i),i);
  239.     }
  240.  
  241.     right_tmp=new Vector();
  242.     right_tmp.setSize(right_items.size());
  243.     for(i=0; i<right_items.size(); ++i) {
  244.       right_tmp.setElementAt(right_items.elementAt(i),i);
  245.     }
  246.  
  247.     /* make the hotspots */
  248.     left_hotspots=new Vector();
  249.     right_hotspots=new Vector();
  250.  
  251.     /* draw the bar */
  252.     bar_image=cs.make_menubar_images(left_items,right_items,w,
  253.                    derive_font(style_manager.default_font()),
  254.                    left_hotspots, right_hotspots);
  255.  
  256.     /* now the images are in left_items and right_items */
  257.     left_images=left_items;
  258.     right_images=right_items;
  259.  
  260.     /* now we can put the items back */
  261.     left_items=left_tmp;
  262.     right_items=right_tmp;
  263.  
  264.     /* can't resize in y */
  265.     set_intrinsic_h(bar_image.height());
  266.   }
  267.   
  268.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  269.  
  270.   /**
  271.    * Draw the object on a given drawable.
  272.    * @param drawable d the surface to draw on 
  273.    */
  274.   public void draw_self_local(drawable d)  {
  275.     loaded_image down_image;
  276.     style cs;
  277.     int offset;
  278.     Rectangle r;
  279.  
  280.     /* can't make a menubar arbitrarily small */
  281.     if (bar_image==null) {
  282.       return;
  283.     }
  284.     d.drawImage(bar_image,0,0);
  285.  
  286.     /* check for possible selection */
  287.     if (currently_depressed()!=-1) {
  288.  
  289.       /* get the image... is it left or right side? */
  290.       if (current_is_left()) {
  291.     down_image=(loaded_image)left_images.
  292.       elementAt(currently_depressed());
  293.     r=(Rectangle)left_hotspots.elementAt(currently_depressed());
  294.       } else {
  295.     down_image=(loaded_image)right_images.
  296.       elementAt(currently_depressed());
  297.     r=(Rectangle)right_hotspots.elementAt(currently_depressed());
  298.       }
  299.  
  300.       /* find out the offset from the style system */
  301.       cs=style_manager.current_style();
  302.       offset=cs.menubar_image_shift();
  303.  
  304.       /* got the image and the location so blit it */
  305.       d.drawImage(down_image,r.x,offset);
  306.     }
  307.   }
  308.   
  309.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  310.  
  311.   /** 
  312.    * Indicate that we intrinsically constrain height.
  313.    * Thus, it is not modifiable by either the programmer or user.
  314.    * @return int return the constant indicating the correct intrinsic dimension
  315.    */
  316.   public int intrinsic_constraints() {
  317.     return H ;
  318.   }
  319.   
  320.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  321.  
  322.   /** 
  323.    * Dispatch mouse button press input to the object.  Return true if
  324.    * this event was consumed. 
  325.    * 
  326.    * @param event  evt       the event to dispatch.
  327.    * @param Object user_info the pick-time value.
  328.    */
  329.   public boolean press(event evt, Object user_info) {
  330.     /* walk the hotspots, looking for this coord */
  331.     int i,selection=0;
  332.     boolean got_it=false,on_left=true;
  333.     Rectangle r;
  334.  
  335.     /* if we don't have any image, we can't do anything */
  336.     if (bar_image==null) return false;
  337.  
  338.     /* look at left hand hotspots */
  339.     for (i=0; i<left_hotspots.size(); ++i) {
  340.       r=(Rectangle)left_hotspots.elementAt(i);
  341.       /* inside? */
  342.       if ((evt.local_x()>=r.x) &&
  343.       (evt.local_x()<=r.x+r.width) &&
  344.       (evt.local_y()>=r.y) &&
  345.       (evt.local_y()<=r.y+r.height)) {
  346.     /* it is inside */
  347.     got_it=true;
  348.     selection=i;
  349.     break;
  350.       }
  351.     }
  352.  
  353.     /* if its not a left one, its might be a right one */
  354.     if (!got_it) {
  355.       /* look at left hand hotspots */
  356.       for (i=0; i<right_hotspots.size(); ++i) {
  357.     r=(Rectangle)right_hotspots.elementAt(i);
  358.     /* inside? */
  359.     if ((evt.local_x()>=r.x) &&
  360.         (evt.local_x()<=r.x+r.width) &&
  361.         (evt.local_y()>=r.y) &&
  362.         (evt.local_y()<=r.y+r.height)) {
  363.       /* it is inside */
  364.       got_it=true;
  365.       on_left=false;
  366.       selection=i;
  367.     }
  368.       }
  369.     }
  370.  
  371.     /* if they didn't press on anything, we still want to consume the
  372.        event so stuff behind us doesn't end up with it */
  373.     if (got_it==false) {
  374.       return true;
  375.     }
  376.  
  377.     /* set the menu to go down */
  378.     set_currently_depressed(selection,on_left,evt);
  379.  
  380.     /* we're done, the menu agent will handle it from here */
  381.     return true;
  382.   }
  383.   
  384.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  385.  
  386.   /** 
  387.    * Dispatch mouse button release to the object.  Return true if event 
  388.    * was consumed.  
  389.    * @param event  evt       the event to dispatch.
  390.    * @param Object user_info the pick-time value.
  391.    */
  392.   public boolean release(event evt, Object user_info) {
  393.     return false;
  394.   }
  395.   
  396.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  397.  
  398.   /**
  399.    * Set the menu associated with a particular item (String or
  400.    * loaded_image). If you try to set a menu for a value that is not in the
  401.    * current set of objects, we ignore this call. This call
  402.    * will break if you have multiple items on the menu which 
  403.    * have the same string; however, this is so bad from a UI design
  404.    * standpoint that I am not going to worry about it.
  405.    *
  406.    * @param Object obj the menu item's string or image.
  407.    * @param menu   m   the menu to associate with that item.
  408.    */
  409.   public void associate_menu(Object obj, menu m) {
  410.  
  411.     /* look for a left item set */
  412.     if (left_items.indexOf(obj)!=-1) {
  413.       button_to_menu.put(obj,m);
  414.       return;
  415.     }
  416.  
  417.     /* might be on the right */
  418.     if (right_items.indexOf(obj)!=-1) {
  419.       button_to_menu.put(obj,m);
  420.       return;
  421.     }
  422.     /* we just ignore calls which don't correspond to things that are
  423.        on the menu */
  424.   }
  425.   
  426.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  427.  
  428.   /**
  429.    * This function computes the menu location given a hotspot rectangle
  430.    * in our coordinate system. This is here as a hook for easy overriding
  431.    * in case you want to put a menubar at the bottom or sides of the
  432.    * screen.
  433.    * 
  434.    * @param Rectangle r the rectangle of the hotspot the user moused on.
  435.    * @param menu      m the menu to pop up.
  436.    * @return Point the point to place the menu at (in the coord sys of the 
  437.    *               top level that this menubar is in).
  438.    */
  439.   public Point compute_menu_location(Rectangle r,menu m) {
  440.  
  441.     /* get this in the coord system of the top level */
  442.     Point p=local_to_global(new Point(r.x,r.y));
  443.  
  444.     /* now just put it just below the rectangle */
  445.     p.y+=h();
  446.  
  447.     /* make sure it fits in the top_level */
  448.     if (p.x+m.w()>get_top_level().w()) {
  449.       /* right justify it if it didn't fit */
  450.       p.x=get_top_level().w()-m.w();
  451.     }
  452.  
  453.     return p;
  454.   }
  455.   
  456.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  457.  
  458.   /**
  459.    * This function gets called when the menubar's popped down menu
  460.    * gets finished. This function sets the menu bar back to its default
  461.    * state.
  462.    */
  463.   public void menu_done() {
  464.     _currently_depressed=-1;
  465.     damage_self();
  466.   }
  467.   
  468.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  469.  
  470.   /**
  471.    * This function is called to alert the notifier that the interaction
  472.    * is now over their area. The notifier should return true if it
  473.    * modified the set of objects in the menu focus in response to
  474.    * this call.
  475.    *
  476.    * @param int   x   the x coordinate (in the notifiers coordinate system) of 
  477.               the cursor.
  478.    * @param int   y   the y coordinate (in the notifiers coordinate system) of 
  479.    *                  the cursor.
  480.    * @param event evt the event we are testing.
  481.    * @return boolean true if the notifier modified the focus set of the menu 
  482.    *                 agent in response to this.
  483.    */
  484.   public boolean menu_modify(int x,int y,event evt) {
  485.     int i;
  486.     Rectangle r;
  487.     boolean is_left=true;
  488.     int selection=-1;
  489.  
  490.     /* look at the left hotspots */
  491.     for (i=0; i<left_hotspots.size(); ++i) {
  492.       r=(Rectangle)left_hotspots.elementAt(i);
  493.       /* inside? */
  494.       if ((x>=r.x) && (x<=r.x+r.width) &&
  495.       (y>=r.y) && (y<=r.y+r.height)) {
  496.     selection=i;
  497.     break;
  498.       }
  499.     }
  500.  
  501.     /* did we find it on the left */
  502.     if (selection==-1) {
  503.       /* look at the right hotspots */
  504.       for (i=0; i<right_hotspots.size(); ++i) {
  505.     r=(Rectangle)right_hotspots.elementAt(i);
  506.     /* inside? */
  507.     if ((x>=r.x) && (x<=r.x+r.width) &&
  508.         (y>=r.y) && (y<=r.y+r.height)) {
  509.       selection=i;
  510.       is_left=false;
  511.       break;
  512.     }
  513.       }
  514.     }
  515.  
  516.     /* did we find it at all? */
  517.     if (selection==-1) return false;
  518.  
  519.     /* did we find it but its already down? */
  520.     if ((selection==currently_depressed()) &&
  521.     (is_left==current_is_left())) return false;
  522.  
  523.     /* clear the focus set */
  524.     menu.agent().clear_focus(evt);
  525.  
  526.     /* depress this object */
  527.     set_currently_depressed(selection,is_left,evt);
  528.  
  529.     /* its ok , we fixed it */
  530.     return true;
  531.   }
  532.   
  533.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  534.  
  535.   /** 
  536.    * Configure the object to ensure it has its proper bounds and has declared
  537.    * all its damage.
  538.    */
  539.   public void configure() {
  540.     super.configure();
  541.     /* if we are a different width, recompute the image */
  542.     if (w()!=cached_width) {
  543.       cached_width=w();
  544.       style_changed();  
  545.     }
  546.   }
  547.   
  548.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  549. }
  550. /*=========================== COPYRIGHT NOTICE ===========================
  551.  
  552. This file is part of the subArctic user interface toolkit.
  553.  
  554. Copyright (c) 1996 Scott Hudson and Ian Smith
  555. All rights reserved.
  556.  
  557. The subArctic system is freely available for most uses under the terms
  558. and conditions described in 
  559.   http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html 
  560. and appearing in full in the lib/interactor.java source file.
  561.  
  562. The current release and additional information about this software can be 
  563. found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
  564.  
  565. ========================================================================*/
  566.